Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Displaying Raw • Download


meshchatx/src/backend/http_url_guard.py cdf2a21897e07b93e0c48ec5e9ba769f216eec06 (cdf2a218) Text, 3.84 KB

T8b949e# SPDX-License-Identifier: 0BSD

T8b949e"""Shared HTTP(S) URL validation for outbound client requests (e.g. LibreTranslate)."""

Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations

Tff7b72import T7ee787ipaddress
Tff7b72import T7ee787re
Tff7b72from T7ee787urllibT7ee787.T7ee787parse Tff7b72import Te6edf3unquoteTb4b4b4, Te6edf3urlparseTb4b4b4, Te6edf3urlunparse


Tff7b72class T56d364UnsafeOutboundUrlErrorTb4b4b4(Tf85149ValueErrorTb4b4b4)Tb4b4b4:
T8b949e"""Raised when a URL is not permitted for server-side fetch."""


Tff7b72def Td2a8ffnormalize_loopback_http_service_baseTb4b4b4(Te6edf3urlTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657strTb4b4b4:
T8b949e"""Return scheme://host:port with no path, query, or fragment.

Only ``http``/``https`` to loopback hosts (127.0.0.1, localhost, ::1) are allowed.
Userinfo (embedded credentials) is rejected.
"""
Tff7b72if Tff7b72not Te6edf3url Tff7b72or Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3urlTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must be a non-empty stringTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3parsed Tff7b72= Te6edf3urlparseTb4b4b4(Te6edf3urlTff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72if Te6edf3parsedTff7b72.Td2a8ffscheme Tff7b72not Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffhttpTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffhttpsTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must use http or httpsTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3netloc Tff7b72= Te6edf3parsedTff7b72.Td2a8ffnetloc Tff7b72or Ta5d6ff"Ta5d6ff"
Tff7b72if Ta5d6ff"Ta5d6ff@Ta5d6ff" Tff7b72in Te6edf3netlocTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must not contain credentialsTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3host Tff7b72= Te6edf3parsedTff7b72.Td2a8ffhostname
Tff7b72if Te6edf3host Tff7b72is Tff7b72NoneTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must include a hostnameTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3host_norm Tff7b72= Te6edf3hostTff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Ta5d6ff"Ta5d6ff[]Ta5d6ff"Tb4b4b4)
Tff7b72if Te6edf3host_norm Tff7b72not Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ff127.0.0.1Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fflocalhostTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff::1Ta5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL host must be 127.0.0.1, localhost, or ::1Ta5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3authority Tff7b72= Te6edf3netloc
Te6edf3origin Tff7b72= Te6edf3urlunparseTb4b4b4(Tb4b4b4(Te6edf3parsedTff7b72.Td2a8ffschemeTb4b4b4, Te6edf3authorityTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3originTff7b72.Td2a8ffrstripTb4b4b4(Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)


Te6edf3_WS_CTRL Tff7b72= Te6edf3reTff7b72.Td2a8ffcompileTb4b4b4(Ta5d6ffrTa5d6ff"Ta5d6ff[Ta5d6ff\Ta5d6ffx00-Ta5d6ff\Ta5d6ffx20Ta5d6ff\Ta5d6ffx7f]Ta5d6ff"Tb4b4b4)


Tff7b72def Td2a8ffnormalize_libretranslate_http_service_baseTb4b4b4(Te6edf3urlTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657strTb4b4b4:
T8b949e"""Return scheme://host:port with no path, query, or fragment.

Accepts any HTTP(S) hostname or IP reachable from this process (remote LibreTranslate or
public API). Embedded credentials are rejected; non-http(s) schemes are rejected.

Literal IPv4 link-local targets (``169.254.0.0/16``) are rejected as a common SSRF/metadata
path. Other private or loopback addresses are allowed so local servers and overlays (e.g. VPN
mesh) continue to work.
"""
Tff7b72if Tff7b72not Te6edf3url Tff7b72or Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3urlTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must be a non-empty stringTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3trimmed Tff7b72= Te6edf3urlTff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3_WS_CTRLTff7b72.Td2a8ffsearchTb4b4b4(Te6edf3trimmedTb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must not contain whitespace or control charactersTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3parsed Tff7b72= Te6edf3urlparseTb4b4b4(Te6edf3trimmedTb4b4b4)
Tff7b72if Te6edf3parsedTff7b72.Td2a8ffscheme Tff7b72not Tff7b72in Tb4b4b4(Ta5d6ff"Ta5d6ffhttpTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffhttpsTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must use http or httpsTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3netloc Tff7b72= Te6edf3parsedTff7b72.Td2a8ffnetloc Tff7b72or Ta5d6ff"Ta5d6ff"
Tff7b72if Ta5d6ff"Ta5d6ff@Ta5d6ff" Tff7b72in Te6edf3netlocTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must not contain credentialsTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3host Tff7b72= Te6edf3parsedTff7b72.Td2a8ffhostname
Tff7b72if Te6edf3host Tff7b72is Tff7b72NoneTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must include a hostnameTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3host_decoded Tff7b72= Te6edf3unquoteTb4b4b4(Te6edf3hostTb4b4b4, Te6edf3errorsTff7b72=Ta5d6ff"Ta5d6ffstrictTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3_WS_CTRLTff7b72.Td2a8ffsearchTb4b4b4(Te6edf3host_decodedTb4b4b4)Tb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must not contain whitespace or control charactersTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3host_for_ip_check Tff7b72= Te6edf3host_decodedTff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffstripTb4b4b4(Ta5d6ff"Ta5d6ff[]Ta5d6ff"Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3addr Tff7b72= Te6edf3ipaddressTff7b72.Td2a8ffip_addressTb4b4b4(Te6edf3host_for_ip_checkTb4b4b4)
Tff7b72except Tf85149ValueErrorTb4b4b4:
Tff7b72pass
Tff7b72elseTb4b4b4:
Tff7b72if Te6edf3addrTff7b72.Td2a8ffversion Tff7b72== T79c0ff4 Tff7b72and Te6edf3addrTff7b72.Td2a8ffis_link_localTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must not target an IPv4 link-local addressTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72if Te6edf3addrTff7b72.Td2a8ffis_multicast Tff7b72or Te6edf3addrTff7b72.Td2a8ffis_unspecifiedTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must not target a multicast or unspecified addressTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)
Tff7b72if Te6edf3addrTff7b72.Td2a8ffis_reservedTb4b4b4:
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffURL must not target a reserved addressTa5d6ff"
Tff7b72raise Te6edf3UnsafeOutboundUrlErrorTb4b4b4(Te6edf3msgTb4b4b4)

Te6edf3authority Tff7b72= Te6edf3netloc
Te6edf3origin Tff7b72= Te6edf3urlunparseTb4b4b4(Tb4b4b4(Te6edf3parsedTff7b72.Td2a8ffschemeTb4b4b4, Te6edf3authorityTb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3originTff7b72.Td2a8ffrstripTb4b4b4(Ta5d6ff"Ta5d6ff/Ta5d6ff"Tb4b4b4)


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────